#include "common.h"
int main(int argc, char *argv[])
{

  //--------------------------------------------------------------------
  // 1. Prologue
  //--------------------------------------------------------------------

  cout << endl
       << flush;
  cout << endl
       << flush;
  cout << "----------------------------------------------------------------" << endl
       << flush;
  cout << endl
       << flush;
  std::map<std::string, std::string> point_to_connector_list;
  read_csv("input.csv", point_to_connector_list);
  // --- Creating the Session:  A session must always be created in a batch
  char *sessionName = "CAA_EhiNetwork_Session";
  CATSession *pSession = NULL;
  HRESULT rc = ::Create_Session(sessionName, pSession);
  if ((SUCCEEDED(rc)) && (NULL != pSession))
  {
    cout << "> session created : " << sessionName << endl
         << flush;
  }
  else
  {
    cout << "ERROR in creating session" << sessionName << endl
         << flush;
    return 1;
  }

  // --- Opening an existing document
  //     The input parameter to this sample program must contain
  //     the entire path and name of the document that is to be opened.

  CATDocument *pDoc = NULL; // pDoc is a pointer to the document
  std::string filename;
  if (argc > 1)
  {
    filename = argv[1];
  }
  else
  {
    filename = "original.CATProduct";
  }
  cout << "> open document :" << filename << endl
       << flush;
  rc = CATDocumentServices::OpenDocument(filename.c_str(), pDoc);
  if (SUCCEEDED(rc) && (NULL != pDoc))
  {
    cout << "> document opened " << endl
         << flush;
  }
  else
  {
    cout << "ERROR in opening document" << endl
         << flush;
    return 2;
  }

  // --- Retrieving root product of the opened document

  CATIProduct *piRootProduct = NULL; // piRootProduct is a handle to document root product
  //
  CATIDocRoots *piDocRoots = NULL;
  rc = pDoc->QueryInterface(IID_CATIDocRoots, (void **)&piDocRoots);
  if (FAILED(rc) || (NULL == piDocRoots))
    return 3;
  CATListValCATBaseUnknown_var *pListRootProduct = piDocRoots->GiveDocRoots();
  piDocRoots->Release();
  piDocRoots = NULL;

  CATBaseUnknown_var spCBU((*pListRootProduct)[1]);
  if (NULL_var != spCBU && pListRootProduct && pListRootProduct->Size())
  {
    rc = spCBU->QueryInterface(IID_CATIProduct, (void **)&piRootProduct);
    if (pListRootProduct)
      delete pListRootProduct;
    pListRootProduct = NULL;
    //
    if (SUCCEEDED(rc) && (NULL != piRootProduct))
      cout << "> root product found in document " << endl
           << flush;
    else
    {
      cout << "ERROR : Root product not found in document " << endl
           << flush;
      return 3;
    }
  }

  //--------------------------------------------------------------------
  // 2. Initializing Electrical Environment
  //--------------------------------------------------------------------

  CATIEleDocServices *piElecDocServices = NULL;
  rc = pDoc->QueryInterface(IID_CATIEleDocServices, (void **)&piElecDocServices);
  if (SUCCEEDED(rc) && piElecDocServices)
  {
    rc = piElecDocServices->Initialize();
  }

  if (SUCCEEDED(rc))
  {
    cout << "> electrical environment initialized " << endl
         << flush;
  }
  else
  {
    cout << "ERROR : electrical environment initialization failed " << endl
         << flush;
    return 4;
  }

  if (NULL != piElecDocServices)
  {
    piElecDocServices->Release();
    piElecDocServices = NULL;
  }
  CATIEhiGeoBundle *piGeoBundle = NULL;
  if (NULL_var == spCBU || FAILED(piRootProduct->QueryInterface(IID_CATIEhiGeoBundle, (void **)&piGeoBundle)))
  {
    return -1;
  }
  //--------------------------------------------------------------------
  // 3. Retrieving all Electrical Products under Root Product
  //    and Selecting one Bundle Segment
  //--------------------------------------------------------------------

  // --- Retrieving all products under root product

  CATListValCATBaseUnknown_var *pListProduct = NULL;
  pListProduct = piRootProduct->GetAllChildren(CATIProduct::ClassName());
  piRootProduct->Release();
  piRootProduct = NULL;

  int NumberOfProduct = 0;
  if ((NULL != pListProduct) && pListProduct->Size())
  {
    NumberOfProduct = pListProduct->Size();
    cout << "> number of products found : " << NumberOfProduct << endl
         << flush;
  }
  else
  {
    cout << "ERROR : no product found in assembly " << endl
         << flush;
    return 5;
  }

  // --- Computing list of Electrical Network Objects from list of products

  CATListValCATBaseUnknown_var *pListNetworkObject = new CATLISTV(CATBaseUnknown_var);
  int NumberOfNetworkObject = 0;
  CATIEhiNetwork *piNetworkObject = NULL;
  int i = 0;
  CATIEhiMultiBranchable *piMultiBranchable = NULL;
  for (i = 1; i <= NumberOfProduct; i++)
  {
    spCBU = (*pListProduct)[i];
    if (NULL_var != spCBU && SUCCEEDED(spCBU->QueryInterface(IID_CATIEhiNetwork, (void **)&piNetworkObject)) && piNetworkObject)
    {
      pListNetworkObject->Append((*pListProduct)[i]);
      piNetworkObject->Release();
      piNetworkObject = NULL;
    }
    else if (SUCCEEDED(spCBU->QueryInterface(IID_CATIEhiMultiBranchable, (void **)&piMultiBranchable)))
    {
      cout << "Multibranchable mode" << endl;
    }
  }

  delete pListProduct;
  pListProduct = NULL;

  // scanning connectors
  std::map<std::string, CATIProduct *> connectors_list;
  if (NULL != pListNetworkObject)
  {
    NumberOfNetworkObject = pListNetworkObject->Size();
    cout << "> number of electrical object in network :" << NumberOfNetworkObject << endl
         << flush;

    CATIElecAttrAccess *piElecAttribute = NULL;
    CATUnicodeString ElecType = "unknown";
    CATIProduct *piInstanceProduct = NULL;
    CATUnicodeString InstanceName = "unknown";

    for (i = 1; i <= NumberOfNetworkObject; i++)
    {
      spCBU = (*pListNetworkObject)[i];
      if (NULL_var != spCBU && SUCCEEDED(spCBU->QueryInterface(IID_CATIElecAttrAccess, (void **)&piElecAttribute)))
      {
        piElecAttribute->GetElecType(ElecType);
        piElecAttribute->Release();
        piElecAttribute = NULL;
      }
      if (NULL_var != spCBU && SUCCEEDED(spCBU->QueryInterface(IID_CATIProduct, (void **)&piInstanceProduct)))
      {
        piInstanceProduct->GetPrdInstanceName(InstanceName);
        piInstanceProduct->Release();
        piInstanceProduct = NULL;
      }
      std::string name = InstanceName.ConvertToChar();
      CATIProduct *connector_i = NULL;
      spCBU->QueryInterface(IID_CATIProduct, (void **)&connector_i);
      connectors_list[name] = connector_i;
      cout << "   " << InstanceName.ConvertToChar() << " -> type = " << ElecType.ConvertToChar() << endl
           << flush;
      ElecType = "unknown";
      InstanceName = "unknown";
    }
  }
  else
  {
    cout << "ERROR : no object found  " << endl
         << flush;
    return 6;
  }

  // --- check the multi branchable object

  CATListValCATBaseUnknown_var *pListBundleSegs = NULL;
  if (piMultiBranchable == NULL)
  {
    return -1;
  }
  if (SUCCEEDED(piMultiBranchable->ListBundleSegments(&pListBundleSegs)))
  {
    cout << "num of bundle segments in multi-branchable " << pListBundleSegs->Size() << endl;
  }
  else
  {
    cout << "ERROR : no bundle segments found  " << endl;
    return 7;
  }

  //--------------------------------------------------------------------
  // 4. Interating over all bundle segments and repairing not connected endpoints
  //--------------------------------------------------------------------

  CATIProduct *connector_end = NULL;
  CATIProduct *connector_start = NULL;
  CATIEhiBnlSegmentExtremity *Extremity1 = NULL;
  CATIEhiBnlSegmentExtremity *Extremity2 = NULL;

  for (int jj = 1; jj <= pListBundleSegs->Size(); jj++)
  {
    CATIEhiBundleSegment *piBundleSegment = NULL;
    rc = (*pListBundleSegs)[jj]->QueryInterface(IID_CATIEhiBundleSegment, (void **)&piBundleSegment);
    piBundleSegment->GetExtremities(&Extremity1, &Extremity2);
    std::string start_name, end_name;
    get_extremity_name(Extremity1, start_name);
    get_extremity_name(Extremity2, end_name);
    set_connector(connectors_list, point_to_connector_list, start_name, connector_start);
    set_connector(connectors_list, point_to_connector_list, end_name, connector_end);
    cout << "segment " << jj << "start with " << start_name << ", end with" << end_name << endl;
    CATIEhiNetworkExtremity *piNetworkExtremity = NULL;
    for (i = 1; i <= 2; i++)
    {
      CATListValCATBaseUnknown_var *pListConnected = NULL;
      if (i == 1)
      {
        spCBU = Extremity1;
      }
      else
      {
        spCBU = Extremity2;
      }
      if (NULL_var != spCBU)
        spCBU->QueryInterface(IID_CATIEhiNetworkExtremity, (void **)&piNetworkExtremity);
      if (NULL != piNetworkExtremity)
      {
        piNetworkExtremity->ListConnectedConnectorPoints(&pListConnected);
        if (NULL != pListConnected)
        {
          continue;
        }
        else if (i == 1)
        {
          rc = linkpoint(Extremity1, connector_start, piBundleSegment, piMultiBranchable, false);
          connector_start->Release();
          connector_start = NULL;
        }
        else if (i == 2)
        {
          linkpoint(Extremity2, connector_end, piBundleSegment, piMultiBranchable, true);
          connector_end->Release();
          connector_end = NULL;
        }
      }

      piNetworkExtremity->Release();
      piNetworkExtremity = NULL;
    }
  }
  // recompute the geometry
  if (FAILED(piGeoBundle->ComputeMultiBranchable(piMultiBranchable)))
  {
    return -1;
  }

  CATIProduct_var spBnsInstance;     // bundle segment instance product
  CATIProduct_var spBnsReference;    // bundle segment reference product
  CATILinkableObject_var spLinkable; // handle to retrieve document from object
  CATDocument *pBnsPartDoc = NULL;   // bundle segment part document

  // --- saving bundle segment 1 CATPart document
  spBnsInstance = piMultiBranchable;
  if (NULL_var != spBnsInstance)
    spBnsReference = spBnsInstance->GetReferenceProduct();
  if (NULL_var != spBnsReference)
    spLinkable = spBnsReference;
  if (NULL_var != spLinkable)
    pBnsPartDoc = spLinkable->GetDocument();
  CATUnicodeString StorageNameDocument;
  StorageNameDocument = pBnsPartDoc->StorageName();
  rc = CATDocumentServices::SaveAs(*pBnsPartDoc, StorageNameDocument);
  if (SUCCEEDED(rc))
  {
    cout << "modified multibrachable saved successfully" << endl;
  }
  else
  {
    cout << "ERROR in saving modified multibrachable" << endl;
  }
  if (NULL != piMultiBranchable)
    piMultiBranchable->Release();
  piMultiBranchable = NULL;

  //--------------------------------------------------------------------
  // 6. Epilogue
  //--------------------------------------------------------------------
  if (pListNetworkObject)
    delete pListNetworkObject;
  pListNetworkObject = NULL;
  // --- removing document from session
  StorageNameDocument = pDoc->StorageName();
  rc = CATDocumentServices::SaveAs(*pDoc, StorageNameDocument);
  rc = CATDocumentServices::Remove(*pDoc);
  if (SUCCEEDED(rc))
  {
    cout << "> document removed " << endl
         << flush;
  }
  else
  {
    cout << "ERROR in removing document" << endl
         << flush;
    return 14;
  }

  // --- closing the session
  rc = ::Delete_Session(sessionName);
  if (SUCCEEDED(rc))
    cout << "> session deleted " << endl
         << flush;
  else
  {
    cout << "ERROR in deleting session" << endl
         << flush;
    return 15;
  }
  cout << "----------------------------------------------------------------" << endl
       << flush;
  cout << endl
       << flush;
  cout << endl
       << flush;

  //
  return (0);
}
